Scrapy de sitios con R

rm(list = ls())      # Para limpiar memoria
# Cargar librerías necesarias para reatlizar solicitudes HTTP, parseo de HTML, manipulación de datos y manejo de texto
library(httr)      # Para realizar solicitudes HTTP
library(rvest)     # Para extraer y parsear contenido HTML
library(dplyr)     # Para manipulación de datos
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
library(jsonlite)  # Para manejar datos JSON
library(stringr)   # Para trabajar con cadenas de texto
library(openssl)   # Para codificar base64
## Linking to: OpenSSL 3.0.2 15 Mar 2022
library(lubridate) # Para fechas
## 
## Attaching package: 'lubridate'
## The following objects are masked from 'package:base':
## 
##     date, intersect, setdiff, union
library(DT)        # Para datatables en la expostacion HTML

Configuración General

# Definimos el número de páginas a scrapear
num_paginas <- 5

# Definimos el agente de usuario para simular una solicitud desde un navegador
agente_usuario <- "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36"

# Función para configurar el agente de usuario en las solicitudes HTTP
configurar_agente_usuario <- function() {
  httr::set_config(httr::user_agent(agente_usuario))
}

Funciones Generales

# Función para crear una lista de URLs a scrapear
# Recibe como entrada una URL base, el número de páginas y una función opcional para parámetros adicionales
crear_lista_paginas <- function(url_base, num_paginas, funcion_parametros = NULL) {
  lista_urls <- vector("list", num_paginas)

  # Iteramos sobre el número de páginas para generar cada URL
  for (i in seq_len(num_paginas)) {
    lista_urls[[i]] <- if (is.null(funcion_parametros)) {
      # Si no se pasa una función para parámetros, concatenamos el número de página a la URL base
      paste0(url_base, i)
    } else {
      # Si se pasa una función, la usamos para generar los parámetros personalizados
      funcion_parametros(url_base, i)
    }
  }

  return(lista_urls)
}

# Función para convertir un texto en un slug amigable para URLs (sin caracteres especiales)
crear_slug <- function(texto) {
  texto %>%
    tolower() %>%                                 # Convertimos el texto a minúsculas
    str_replace_all("[^a-z0-9]+", "-") %>%        # Reemplazamos caracteres no alfanuméricos por guiones
    str_replace_all("(^-|-$)", "")                # Eliminamos guiones al inicio o final
}

Scraping Tiendas

Scraping: Tienda “TOTTO”

scrapy HTML DOM, despues de ver el DOM podemos ver que hay un ajax que trae el html completo de cada pagina y tambien un json dentro del mismo html

Tienda Totto
Tienda Totto
# Función para extraer datos de una página de productos de la tienda TOTTO
extraer_datos_totto <- function(url) {
  # Intentamos leer el contenido HTML de la página
  documento <- tryCatch(
    read_html(url, user_agent = agente_usuario),
    error = function(e) {
      message(paste("Error al cargar:", url))  # Mostramos un mensaje en caso de error
      return(NULL)                             # Retornamos NULL si hay un error
    }
  )

  if (is.null(documento)) return(NULL)         # Si no se pudo cargar el documento, salimos de la función

  print(paste("Extrayendo datos de:", url))

  # Seleccionamos los elementos HTML que contienen los productos
  productos_html <- documento %>% html_elements("div._banner-title")

  # Extraemos y retornamos la información relevante de cada producto
  data.frame(
    url = productos_html %>% html_element("a") %>% html_attr("href"),
    imagen = productos_html %>% html_element("img") %>% html_attr("src"),
    nombre = productos_html %>% html_element("h3 span") %>% html_text(trim = TRUE),
    precio = productos_html %>%
      html_element("span.__item-price") %>%
      html_text(trim = TRUE) %>%
      str_replace_all("Bs. ", "") %>% # quitamos signo "Bs. "
      str_replace_all("\\.", "") %>% # removemos todos los puntos por casos 1.153,00
      str_replace_all(",", ".") %>%  # convertimos comas en puntos
      as.numeric()
    ,
    fecha = today(),
    tienda = "totto",
    stringsAsFactors = FALSE
  )
}

# Función para extraer datos de múltiples páginas de la tienda TOTTO
extraer_datos_sitio_totto <- function(url_base, num_paginas) {
  configurar_agente_usuario() # Configuramos el agente de usuario
  lista_urls <- crear_lista_paginas(url_base, num_paginas)  # Creamos la lista de URLs

  # Extraemos los datos de cada página y los combinamos en un solo data frame
  do.call(
    bind_rows,
    lapply(lista_urls, extraer_datos_totto)
  )
}

Scraping: Tienda Multicenter

scrapy GraphQL, base64encode, urlencode, esta tienda cuemnta con una recarga en “Mosrar mas Productos” Tienda multicenter_01 que cuando inspeccionamos parece ser un graphQL enviando parametros por GET codificado url, procedemos a decodificar y vemos que es un json Tienda multicenter_02 vemos que hay otro parametro que esta encriptado posiblemente base64 Tienda multicenter_03 por lo que decodificamos en base 64 y podemos ver otro JSON Tienda multicenter_04 donde apreciamos parametros de paginacion Tienda multicenter_05 probamos cambiando a otro rango de pagina Tienda multicenter_06 y volvemos a codificar Tienda multicenter_07 Probamos el url en navegador y funciona Tienda multicenter_08

Este sitio nos permite usar parseo JSON, codificacion bas64 y url ademas de transformacion de fecha y precio

# Función para extraer datos de una página de productos de la tienda Multicenter
extraer_datos_multicenter <- function(url) {
  # Intentamos realizar una solicitud GET a la URL
  respuesta <- tryCatch(
    GET(url, user_agent = agente_usuario),
    error = function(e) {
      message(paste("Error al cargar:", url))  # Mostramos un mensaje en caso de error
      return(NULL)                             # Retornamos NULL si hay un error
    }
  )

  if (is.null(respuesta)) return(NULL)         # Si no hay respuesta, salimos de la función

  print(paste("Extrayendo datos de:", url))
  # Parseamos el contenido JSON de la respuesta
  content <- content(respuesta, "text")
  datos_json <- fromJSON(content)

  # Seleccionamos los elementos JSON que contienen los productos
  productos <- datos_json$data$productSearch$products

  # Extraemos y retornamos la información relevante de cada producto
  data.frame(
    url = str_replace_all(productos$link,"portal.vtexcommercestable.com.br","www.multicenter.com"),
    # imagen = productos$items[[1]]$images[[1]]$imageUrl, # FIXME - issue con imagenes
    imagen = '',
    nombre = productos$productName,
    precio = productos$priceRange$sellingPrice$lowPrice,
    fecha = productos$releaseDate %>% substr(1,10) %>% ymd(),
    tienda = "multicenter",
    stringsAsFactors = FALSE
  )
}

# Función para extraer datos de múltiples páginas de la tienda TOTTO
extraer_datos_sitio_multicenter <- function(url_base, num_paginas) {
  configurar_agente_usuario()                   # Configuramos el agente de usuario

  # Creamos la lista de URLs con parámetros personalizados
  lista_urls <- crear_lista_paginas(url_base, num_paginas, function(base, i) {
    desde <- (i - 1) * 50
    hasta <- i * 50
    variables <-paste0(
      '{"hideUnavailableItems":false,"skusFilter":"ALL","simulationBehavior":"skip","installmentCriteria":"MAX_WITHOUT_INTEREST","productOriginVtex":true,"map":"productClusterIds","query":"647","orderBy":"OrderByBestDiscountDESC","from":',
      desde,
      ',"to":',
      hasta,
      ',"selectedFacets":[{"key":"productClusterIds","value":"647"}],"operator":"and","fuzzy":"0","searchState":null,"facetsBehavior":"Static","categoryTreeBehavior":"default","withFacets":false,"advertisementOptions":{"showSponsored":true,"sponsoredCount":3,"advertisementPlacement":"top_search","repeatSponsoredProducts":true}}') %>% 
      base64_encode() # convertimos en base64
    extensions<-paste0(
      '{"persistedQuery":{"version":1,"sha256Hash":"9177ba6f883473505dc99fcf2b679a6e270af6320a157f0798b92efeab98d5d3","sender":"vtex.store-resources@0.x","provider":"vtex.search-graphql@0.x"},"variables":"',
      variables,
      '"}') %>%
      URLencode(reserved = T) # codificamos el url
    paste0(url_base,"?extensions=", extensions)
  })

  # Extraemos los datos de cada página y los combinamos en un solo data frame
  do.call(
    bind_rows,
    lapply(lista_urls, extraer_datos_multicenter)
  )
}

Scraping: Noticias Nacional

Scraping: Noticias “El Deber”

Scrapy JSON by urlParams - Despues de revisar el sitio del deber se puede e irnos a una categoria, podemos ver que el ver mas noticias carga un ajax con un JSON con el cual construye la visualizacion, probrando con los parametros y quitando el slug que corresponde a categoria podemos traer todas las noticias, por paginas que especifiquemos.

# Función para extraer datos de una página de noticias de El Deber
extraer_datos_deber <- function(url) {
  # Intentamos realizar una solicitud GET a la URL
  respuesta <- tryCatch(
    GET(url, user_agent = agente_usuario),
    error = function(e) {
      message(paste("Error al cargar:", url))  # Mostramos un mensaje en caso de error
      return(NULL)                             # Retornamos NULL si hay un error
    }
  )

  if (is.null(respuesta)) return(NULL)         # Si no hay respuesta, salimos de la función

  print(paste("Extrayendo datos de:", url))
  # Parseamos el contenido JSON de la respuesta
  content <- content(respuesta, "text")
  datos_json <- fromJSON(content)

  # Extraemos y retornamos la información relevante de cada noticia
  data.frame(
    fecha = datos_json$PublicationDate %>% ymd_hms(tz = "UTC"), 
    titular = datos_json$Title_en,
    medio = "El Deber",
    url = paste0(
      "https://eldeber.com.bo/",
      crear_slug(datos_json$Nodes_en[1]),
      "/",
      datos_json$Url,
      "_",
      datos_json$Id
    ),
    stringsAsFactors = FALSE
  )
}

# Función para extraer datos de múltiples páginas de noticias de El Deber
extraer_datos_sitio_deber <- function(url_base, num_paginas) {
  configurar_agente_usuario()                   # Configuramos el agente de usuario

  # Creamos la lista de URLs con parámetros personalizados
  lista_urls <- crear_lista_paginas(url_base, num_paginas, function(base, i) {
    desde <- (i - 1) * 50
    hasta <- i * 50
    paste0(base, "?from=", desde, "&to=", hasta)
  })

  # Extraemos los datos de cada página y los combinamos en un solo data frame
  do.call(
    bind_rows,
    lapply(lista_urls, extraer_datos_deber)
  )
}

Scraping: Noticias “La Prensa”

scrapy HTML DOM, Analizando el codigo vemos que la pagina esta desarrollada en Drupal y que ingresando a una página de categoria, si presionamos en paginador este actualiza por ajax la pagina, pero es complicado replicar el ajax por la session. Entonces se puede abrir la pagina en una nueva pestaña con los parametros ?q=views/ajax&page=2 los cuales podemos reutilizar

# Función para extraer datos de una página de noticias de La Prensa
extraer_datos_prensa <- function(url) {
  # Intentamos leer el contenido HTML de la página
  documento <- tryCatch(
    read_html(url, user_agent = agente_usuario),
    error = function(e) {
      message(paste("Error al cargar:", url))  # Mostramos un mensaje en caso de error
      return(NULL)                             # Retornamos NULL si hay un error
    }
  )

  if (is.null(documento)) return(NULL)         # Si no se pudo cargar el documento, salimos de la función

  print(paste("Extrayendo datos de:", url))

  # Seleccionamos los elementos HTML que contienen las noticias
  noticias_html <- documento %>% html_elements("div.views-row")

  # Extraemos y retornamos la información relevante de cada noticia
  data.frame(
    fecha = noticias_html %>%
      html_element("time") %>%
      html_attr("datetime") %>% 
      ymd_hms(tz = "UTC"), # convertimos a campo tipo fecha
    titular = noticias_html %>% html_element("h2 a") %>% html_text(trim = TRUE),
    url = paste0(
      "https://laprensa.bo",
      noticias_html %>% html_element("h2 a") %>% html_attr("href")
    ),
    medio = "La Prensa",
    stringsAsFactors = FALSE
  )
}

# Función para extraer datos de múltiples páginas de noticias de La Prensa
extraer_datos_sitio_prensa <- function(url_base, num_paginas) {
  configurar_agente_usuario()                   # Configuramos el agente de usuario
  lista_urls <- crear_lista_paginas(url_base, num_paginas)  # Creamos la lista de URLs

  # Extraemos los datos de cada página y los combinamos en un solo data frame
  do.call(
    bind_rows,
    lapply(lista_urls, extraer_datos_prensa)
  )
}

Ejecución y Guardado

Podemos ejecutar los scrapers desde aqui

# Definimos las URLs base de cada fuente
url_base_totto <- "https://bo.totto.com/buscapagina?fq=C%3a%2f82%2f&PS=48&sl=ae85c755-2e56-4945-a435-89444d09dcb2&cc=48&sm=0&PageNumber="
url_base_multicenter="https://www.multicenter.com/_v/segment/graphql/v1"
url_base_deber <- "https://eldeber.com.bo/api/news/getMore"
url_base_prensa <- "https://laprensa.bo/politica?q=views/ajax&page="

# Extraemos los datos de cada sitio
datos_productos_totto <- extraer_datos_sitio_totto(url_base_totto, num_paginas)
## [1] "Extrayendo datos de: https://bo.totto.com/buscapagina?fq=C%3a%2f82%2f&PS=48&sl=ae85c755-2e56-4945-a435-89444d09dcb2&cc=48&sm=0&PageNumber=1"
## [1] "Extrayendo datos de: https://bo.totto.com/buscapagina?fq=C%3a%2f82%2f&PS=48&sl=ae85c755-2e56-4945-a435-89444d09dcb2&cc=48&sm=0&PageNumber=2"
## [1] "Extrayendo datos de: https://bo.totto.com/buscapagina?fq=C%3a%2f82%2f&PS=48&sl=ae85c755-2e56-4945-a435-89444d09dcb2&cc=48&sm=0&PageNumber=3"
## [1] "Extrayendo datos de: https://bo.totto.com/buscapagina?fq=C%3a%2f82%2f&PS=48&sl=ae85c755-2e56-4945-a435-89444d09dcb2&cc=48&sm=0&PageNumber=4"
## [1] "Extrayendo datos de: https://bo.totto.com/buscapagina?fq=C%3a%2f82%2f&PS=48&sl=ae85c755-2e56-4945-a435-89444d09dcb2&cc=48&sm=0&PageNumber=5"
datos_productos_multicenter <- extraer_datos_sitio_multicenter(url_base_multicenter, num_paginas)
## [1] "Extrayendo datos de: https://www.multicenter.com/_v/segment/graphql/v1?extensions=%7B%22persistedQuery%22%3A%7B%22version%22%3A1%2C%22sha256Hash%22%3A%229177ba6f883473505dc99fcf2b679a6e270af6320a157f0798b92efeab98d5d3%22%2C%22sender%22%3A%22vtex.store-resources%400.x%22%2C%22provider%22%3A%22vtex.search-graphql%400.x%22%7D%2C%22variables%22%3A%22eyJoaWRlVW5hdmFpbGFibGVJdGVtcyI6ZmFsc2UsInNrdXNGaWx0ZXIiOiJBTEwiLCJzaW11bGF0aW9uQmVoYXZpb3IiOiJza2lwIiwiaW5zdGFsbG1lbnRDcml0ZXJpYSI6Ik1BWF9XSVRIT1VUX0lOVEVSRVNUIiwicHJvZHVjdE9yaWdpblZ0ZXgiOnRydWUsIm1hcCI6InByb2R1Y3RDbHVzdGVySWRzIiwicXVlcnkiOiI2NDciLCJvcmRlckJ5IjoiT3JkZXJCeUJlc3REaXNjb3VudERFU0MiLCJmcm9tIjowLCJ0byI6NTAsInNlbGVjdGVkRmFjZXRzIjpbeyJrZXkiOiJwcm9kdWN0Q2x1c3RlcklkcyIsInZhbHVlIjoiNjQ3In1dLCJvcGVyYXRvciI6ImFuZCIsImZ1enp5IjoiMCIsInNlYXJjaFN0YXRlIjpudWxsLCJmYWNldHNCZWhhdmlvciI6IlN0YXRpYyIsImNhdGVnb3J5VHJlZUJlaGF2aW9yIjoiZGVmYXVsdCIsIndpdGhGYWNldHMiOmZhbHNlLCJhZHZlcnRpc2VtZW50T3B0aW9ucyI6eyJzaG93U3BvbnNvcmVkIjp0cnVlLCJzcG9uc29yZWRDb3VudCI6MywiYWR2ZXJ0aXNlbWVudFBsYWNlbWVudCI6InRvcF9zZWFyY2giLCJyZXBlYXRTcG9uc29yZWRQcm9kdWN0cyI6dHJ1ZX19%22%7D"
## [1] "Extrayendo datos de: https://www.multicenter.com/_v/segment/graphql/v1?extensions=%7B%22persistedQuery%22%3A%7B%22version%22%3A1%2C%22sha256Hash%22%3A%229177ba6f883473505dc99fcf2b679a6e270af6320a157f0798b92efeab98d5d3%22%2C%22sender%22%3A%22vtex.store-resources%400.x%22%2C%22provider%22%3A%22vtex.search-graphql%400.x%22%7D%2C%22variables%22%3A%22eyJoaWRlVW5hdmFpbGFibGVJdGVtcyI6ZmFsc2UsInNrdXNGaWx0ZXIiOiJBTEwiLCJzaW11bGF0aW9uQmVoYXZpb3IiOiJza2lwIiwiaW5zdGFsbG1lbnRDcml0ZXJpYSI6Ik1BWF9XSVRIT1VUX0lOVEVSRVNUIiwicHJvZHVjdE9yaWdpblZ0ZXgiOnRydWUsIm1hcCI6InByb2R1Y3RDbHVzdGVySWRzIiwicXVlcnkiOiI2NDciLCJvcmRlckJ5IjoiT3JkZXJCeUJlc3REaXNjb3VudERFU0MiLCJmcm9tIjo1MCwidG8iOjEwMCwic2VsZWN0ZWRGYWNldHMiOlt7ImtleSI6InByb2R1Y3RDbHVzdGVySWRzIiwidmFsdWUiOiI2NDcifV0sIm9wZXJhdG9yIjoiYW5kIiwiZnV6enkiOiIwIiwic2VhcmNoU3RhdGUiOm51bGwsImZhY2V0c0JlaGF2aW9yIjoiU3RhdGljIiwiY2F0ZWdvcnlUcmVlQmVoYXZpb3IiOiJkZWZhdWx0Iiwid2l0aEZhY2V0cyI6ZmFsc2UsImFkdmVydGlzZW1lbnRPcHRpb25zIjp7InNob3dTcG9uc29yZWQiOnRydWUsInNwb25zb3JlZENvdW50IjozLCJhZHZlcnRpc2VtZW50UGxhY2VtZW50IjoidG9wX3NlYXJjaCIsInJlcGVhdFNwb25zb3JlZFByb2R1Y3RzIjp0cnVlfX0%3D%22%7D"
## [1] "Extrayendo datos de: https://www.multicenter.com/_v/segment/graphql/v1?extensions=%7B%22persistedQuery%22%3A%7B%22version%22%3A1%2C%22sha256Hash%22%3A%229177ba6f883473505dc99fcf2b679a6e270af6320a157f0798b92efeab98d5d3%22%2C%22sender%22%3A%22vtex.store-resources%400.x%22%2C%22provider%22%3A%22vtex.search-graphql%400.x%22%7D%2C%22variables%22%3A%22eyJoaWRlVW5hdmFpbGFibGVJdGVtcyI6ZmFsc2UsInNrdXNGaWx0ZXIiOiJBTEwiLCJzaW11bGF0aW9uQmVoYXZpb3IiOiJza2lwIiwiaW5zdGFsbG1lbnRDcml0ZXJpYSI6Ik1BWF9XSVRIT1VUX0lOVEVSRVNUIiwicHJvZHVjdE9yaWdpblZ0ZXgiOnRydWUsIm1hcCI6InByb2R1Y3RDbHVzdGVySWRzIiwicXVlcnkiOiI2NDciLCJvcmRlckJ5IjoiT3JkZXJCeUJlc3REaXNjb3VudERFU0MiLCJmcm9tIjoxMDAsInRvIjoxNTAsInNlbGVjdGVkRmFjZXRzIjpbeyJrZXkiOiJwcm9kdWN0Q2x1c3RlcklkcyIsInZhbHVlIjoiNjQ3In1dLCJvcGVyYXRvciI6ImFuZCIsImZ1enp5IjoiMCIsInNlYXJjaFN0YXRlIjpudWxsLCJmYWNldHNCZWhhdmlvciI6IlN0YXRpYyIsImNhdGVnb3J5VHJlZUJlaGF2aW9yIjoiZGVmYXVsdCIsIndpdGhGYWNldHMiOmZhbHNlLCJhZHZlcnRpc2VtZW50T3B0aW9ucyI6eyJzaG93U3BvbnNvcmVkIjp0cnVlLCJzcG9uc29yZWRDb3VudCI6MywiYWR2ZXJ0aXNlbWVudFBsYWNlbWVudCI6InRvcF9zZWFyY2giLCJyZXBlYXRTcG9uc29yZWRQcm9kdWN0cyI6dHJ1ZX19%22%7D"
## [1] "Extrayendo datos de: https://www.multicenter.com/_v/segment/graphql/v1?extensions=%7B%22persistedQuery%22%3A%7B%22version%22%3A1%2C%22sha256Hash%22%3A%229177ba6f883473505dc99fcf2b679a6e270af6320a157f0798b92efeab98d5d3%22%2C%22sender%22%3A%22vtex.store-resources%400.x%22%2C%22provider%22%3A%22vtex.search-graphql%400.x%22%7D%2C%22variables%22%3A%22eyJoaWRlVW5hdmFpbGFibGVJdGVtcyI6ZmFsc2UsInNrdXNGaWx0ZXIiOiJBTEwiLCJzaW11bGF0aW9uQmVoYXZpb3IiOiJza2lwIiwiaW5zdGFsbG1lbnRDcml0ZXJpYSI6Ik1BWF9XSVRIT1VUX0lOVEVSRVNUIiwicHJvZHVjdE9yaWdpblZ0ZXgiOnRydWUsIm1hcCI6InByb2R1Y3RDbHVzdGVySWRzIiwicXVlcnkiOiI2NDciLCJvcmRlckJ5IjoiT3JkZXJCeUJlc3REaXNjb3VudERFU0MiLCJmcm9tIjoxNTAsInRvIjoyMDAsInNlbGVjdGVkRmFjZXRzIjpbeyJrZXkiOiJwcm9kdWN0Q2x1c3RlcklkcyIsInZhbHVlIjoiNjQ3In1dLCJvcGVyYXRvciI6ImFuZCIsImZ1enp5IjoiMCIsInNlYXJjaFN0YXRlIjpudWxsLCJmYWNldHNCZWhhdmlvciI6IlN0YXRpYyIsImNhdGVnb3J5VHJlZUJlaGF2aW9yIjoiZGVmYXVsdCIsIndpdGhGYWNldHMiOmZhbHNlLCJhZHZlcnRpc2VtZW50T3B0aW9ucyI6eyJzaG93U3BvbnNvcmVkIjp0cnVlLCJzcG9uc29yZWRDb3VudCI6MywiYWR2ZXJ0aXNlbWVudFBsYWNlbWVudCI6InRvcF9zZWFyY2giLCJyZXBlYXRTcG9uc29yZWRQcm9kdWN0cyI6dHJ1ZX19%22%7D"
## [1] "Extrayendo datos de: https://www.multicenter.com/_v/segment/graphql/v1?extensions=%7B%22persistedQuery%22%3A%7B%22version%22%3A1%2C%22sha256Hash%22%3A%229177ba6f883473505dc99fcf2b679a6e270af6320a157f0798b92efeab98d5d3%22%2C%22sender%22%3A%22vtex.store-resources%400.x%22%2C%22provider%22%3A%22vtex.search-graphql%400.x%22%7D%2C%22variables%22%3A%22eyJoaWRlVW5hdmFpbGFibGVJdGVtcyI6ZmFsc2UsInNrdXNGaWx0ZXIiOiJBTEwiLCJzaW11bGF0aW9uQmVoYXZpb3IiOiJza2lwIiwiaW5zdGFsbG1lbnRDcml0ZXJpYSI6Ik1BWF9XSVRIT1VUX0lOVEVSRVNUIiwicHJvZHVjdE9yaWdpblZ0ZXgiOnRydWUsIm1hcCI6InByb2R1Y3RDbHVzdGVySWRzIiwicXVlcnkiOiI2NDciLCJvcmRlckJ5IjoiT3JkZXJCeUJlc3REaXNjb3VudERFU0MiLCJmcm9tIjoyMDAsInRvIjoyNTAsInNlbGVjdGVkRmFjZXRzIjpbeyJrZXkiOiJwcm9kdWN0Q2x1c3RlcklkcyIsInZhbHVlIjoiNjQ3In1dLCJvcGVyYXRvciI6ImFuZCIsImZ1enp5IjoiMCIsInNlYXJjaFN0YXRlIjpudWxsLCJmYWNldHNCZWhhdmlvciI6IlN0YXRpYyIsImNhdGVnb3J5VHJlZUJlaGF2aW9yIjoiZGVmYXVsdCIsIndpdGhGYWNldHMiOmZhbHNlLCJhZHZlcnRpc2VtZW50T3B0aW9ucyI6eyJzaG93U3BvbnNvcmVkIjp0cnVlLCJzcG9uc29yZWRDb3VudCI6MywiYWR2ZXJ0aXNlbWVudFBsYWNlbWVudCI6InRvcF9zZWFyY2giLCJyZXBlYXRTcG9uc29yZWRQcm9kdWN0cyI6dHJ1ZX19%22%7D"
# Combinamos las tiendas de las distintas fuentes en un solo data frame
datos_productos <- bind_rows(datos_productos_totto, datos_productos_multicenter)

datos_noticias_deber <- extraer_datos_sitio_deber(url_base_deber, num_paginas)
## [1] "Extrayendo datos de: https://eldeber.com.bo/api/news/getMore?from=0&to=50"
## [1] "Extrayendo datos de: https://eldeber.com.bo/api/news/getMore?from=50&to=100"
## [1] "Extrayendo datos de: https://eldeber.com.bo/api/news/getMore?from=100&to=150"
## [1] "Extrayendo datos de: https://eldeber.com.bo/api/news/getMore?from=150&to=200"
## [1] "Extrayendo datos de: https://eldeber.com.bo/api/news/getMore?from=200&to=250"
datos_noticias_prensa <- extraer_datos_sitio_prensa(url_base_prensa, num_paginas)
## [1] "Extrayendo datos de: https://laprensa.bo/politica?q=views/ajax&page=1"
## [1] "Extrayendo datos de: https://laprensa.bo/politica?q=views/ajax&page=2"
## [1] "Extrayendo datos de: https://laprensa.bo/politica?q=views/ajax&page=3"
## [1] "Extrayendo datos de: https://laprensa.bo/politica?q=views/ajax&page=4"
## [1] "Extrayendo datos de: https://laprensa.bo/politica?q=views/ajax&page=5"
# Combinamos las noticias de las distintas fuentes en un solo data frame
datos_noticias <- bind_rows(datos_noticias_deber, datos_noticias_prensa)

# Guardamos los datos extraídos en un archivo
save(datos_productos, datos_noticias, file = "_data/datos_scrapeados.RData")

Visualización de datos

# Mostrar las tablas generadas
str(datos_productos)
## 'data.frame':    490 obs. of  6 variables:
##  $ url   : chr  "https://bo.totto.com/mochila-rayol/p?idsku=32066" "https://bo.totto.com/mochila-acuarela/p?idsku=32046" "https://bo.totto.com/mochila-porta-portatil-daki-ma04ind909-23100-z3g/p?idsku=28429" "https://bo.totto.com/mochila-titanio-20/p?idsku=33102" ...
##  $ imagen: chr  "https://tottobo.vteximg.com.br/arquivos/ids/284347-292-292/MA04ECO002-2326N-4DR_2.jpg?v=638362967867130000" "https://tottobo.vteximg.com.br/arquivos/ids/284264-292-292/MA04ECO001-23260-4DR_2.jpg?v=638362957156730000" "https://tottobo.vteximg.com.br/arquivos/ids/275084-292-292/MA04IND909-23100-Z3G-ECOMMERCE_1.jpg?v=638240997571900000" "https://tottobo.vteximg.com.br/arquivos/ids/289570-292-292/MA04TEK004-2410G-NV0_1.jpg?v=638497569558700000" ...
##  $ nombre: chr  "Mochila  Rayol" "Mochila  Acuarela" "Mochila Porta Portátil Daki" "Mochila Titanio 2.0" ...
##  $ precio: num  519 479 719 849 679 899 889 859 290 699 ...
##  $ fecha : Date, format: "2024-12-09" "2024-12-09" ...
##  $ tienda: chr  "totto" "totto" "totto" "totto" ...
head(datos_productos)
##                                                                                   url
## 1                                    https://bo.totto.com/mochila-rayol/p?idsku=32066
## 2                                 https://bo.totto.com/mochila-acuarela/p?idsku=32046
## 3 https://bo.totto.com/mochila-porta-portatil-daki-ma04ind909-23100-z3g/p?idsku=28429
## 4                               https://bo.totto.com/mochila-titanio-20/p?idsku=33102
## 5                                    https://bo.totto.com/mochila-molyk/p?idsku=32116
## 6                  https://bo.totto.com/mochila-rue-bomper-soccer-win-m/p?idsku=31746
##                                                                                                                 imagen
## 1           https://tottobo.vteximg.com.br/arquivos/ids/284347-292-292/MA04ECO002-2326N-4DR_2.jpg?v=638362967867130000
## 2           https://tottobo.vteximg.com.br/arquivos/ids/284264-292-292/MA04ECO001-23260-4DR_2.jpg?v=638362957156730000
## 3 https://tottobo.vteximg.com.br/arquivos/ids/275084-292-292/MA04IND909-23100-Z3G-ECOMMERCE_1.jpg?v=638240997571900000
## 4           https://tottobo.vteximg.com.br/arquivos/ids/289570-292-292/MA04TEK004-2410G-NV0_1.jpg?v=638497569558700000
## 5           https://tottobo.vteximg.com.br/arquivos/ids/285082-292-292/MA04IND920-23200-B04_1.jpg?v=638362989098300000
## 6           https://tottobo.vteximg.com.br/arquivos/ids/284967-292-292/MJ03SOW005-2320-7J8M_1.jpg?v=638362986707200000
##                             nombre precio      fecha tienda
## 1                   Mochila  Rayol    519 2024-12-09  totto
## 2                Mochila  Acuarela    479 2024-12-09  totto
## 3      Mochila Porta Portátil Daki    719 2024-12-09  totto
## 4              Mochila Titanio 2.0    849 2024-12-09  totto
## 5                   Mochila  Molyk    679 2024-12-09  totto
## 6 Mochila  Rue Bomper Soccer Win M    899 2024-12-09  totto
datatable(datos_productos)
str(datos_noticias)
## 'data.frame':    800 obs. of  4 variables:
##  $ fecha  : POSIXct, format: "2024-12-09 22:40:49" "2024-12-09 22:38:07" ...
##  $ titular: chr  "Alcalde de La Paz se defenderá en libertad en el caso 'Bajo Llojeta'" "Cuatro clubes brillaron en el Nacional Preinfantil e Infantil Clausura de Natación" "Bolivia registra 26 casos de tosferina en lo que va del año" "Trudeau sobrevive a la tercera moción de censura presentada por los conservadores" ...
##  $ medio  : chr  "El Deber" "El Deber" "El Deber" "El Deber" ...
##  $ url    : chr  "https://eldeber.com.bo/pa-s/alcalde-de-la-paz-se-defendera-en-libertad-en-el-caso-bajo-llojeta_394611" "https://eldeber.com.bo/pa-s/cuatro-clubes-brillaron-en-el-nacional-preinfantil-e-infantil-clausura-de-natacion_394616" "https://eldeber.com.bo/pa-s/bolivia-registra-26-casos-de-tosferina-en-lo-que-va-del-ano_394615" "https://eldeber.com.bo/pa-s/trudeau-sobrevive-a-la-tercera-mocion-de-censura-presentada-por-los-conservadores_394614" ...
head(datos_noticias)
##                 fecha
## 1 2024-12-09 22:40:49
## 2 2024-12-09 22:38:07
## 3 2024-12-09 22:24:00
## 4 2024-12-09 22:06:00
## 5 2024-12-09 22:03:07
## 6 2024-12-09 21:53:00
##                                                                              titular
## 1               Alcalde de La Paz se defenderá en libertad en el caso 'Bajo Llojeta'
## 2 Cuatro clubes brillaron en el Nacional Preinfantil e Infantil Clausura de Natación
## 3                        Bolivia registra 26 casos de tosferina en lo que va del año
## 4  Trudeau sobrevive a la tercera moción de censura presentada por los conservadores
## 5                 Víctor Muriel, campeón de la Copa Simón Bolívar Femenina de Futsal
## 6    Nallar cumple sentencia aislado en un pabellón de máxima seguridad de Palmasola
##      medio
## 1 El Deber
## 2 El Deber
## 3 El Deber
## 4 El Deber
## 5 El Deber
## 6 El Deber
##                                                                                                                     url
## 1                 https://eldeber.com.bo/pa-s/alcalde-de-la-paz-se-defendera-en-libertad-en-el-caso-bajo-llojeta_394611
## 2 https://eldeber.com.bo/pa-s/cuatro-clubes-brillaron-en-el-nacional-preinfantil-e-infantil-clausura-de-natacion_394616
## 3                        https://eldeber.com.bo/pa-s/bolivia-registra-26-casos-de-tosferina-en-lo-que-va-del-ano_394615
## 4  https://eldeber.com.bo/pa-s/trudeau-sobrevive-a-la-tercera-mocion-de-censura-presentada-por-los-conservadores_394614
## 5                  https://eldeber.com.bo/pa-s/victor-muriel-campeon-de-la-copa-simon-bolivar-femenina-de-futsal_394613
## 6    https://eldeber.com.bo/pa-s/nallar-cumple-sentencia-aislado-en-un-pabellon-de-maxima-seguridad-de-palmasola_394612
datatable(datos_noticias)